package com.zl.basis.http;

import com.alibaba.fastjson.TypeReference;
import com.zl.basis.log.TimberUtil;
import com.zl.basis.util.JsonUtil;
import com.zl.basis.util.StringUtil;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.BufferedSink;

public class HttpUtil {
    private volatile static OkHttpClient mClient;
    private volatile static OkHttpClient mFastClient;

    public static OkHttpClient getClient() {
        if (null == mClient) {
            synchronized (OkHttpClient.class) {
                if (null == mClient) {
                    OkHttpClient.Builder builder = new OkHttpClient.Builder();
                    builder.writeTimeout(10, TimeUnit.SECONDS);
                    builder.readTimeout(10, TimeUnit.SECONDS);
                    builder.connectTimeout(10, TimeUnit.SECONDS);
//                    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
//                    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
//                    builder.addInterceptor(loggingInterceptor);
                    builder.addInterceptor(new TimeCalibrationInterceptor());
//                    builder.dns(FaceRecognition.getOkHttpDns());
                    mClient = builder.build();
                }
            }
        }
        return mClient;
    }

    public static void doGet(String url, Map<String, String> params, Map<String, String> headers, HttpCallBack callBack) {
        String reqUrl = url;
        if (params != null && params.size() > 0)
            reqUrl = reqUrl + "?" + parseUrlRequest(params);
        Request.Builder builder = new Request.Builder();
        builder.url(reqUrl);
        if (headers != null && headers.size() > 0)
            builder.headers(getHeaders(headers));
        request(builder.build(), callBack);
    }

    public static void doPost(String url, Map<String, String> params, HttpCallBack callBack) {
        requestPost(url, getBody(params), null, callBack);
    }

    public static <T> T doPost(String url, Map<String, String> params, TypeReference<T> type) {
        return requestPost(url, getBody(params), null, type);
    }

    public static void doPost(String url, Map<String, String> params, Map<String, String> heardes, HttpCallBack callBack) {
        requestPost(url, getBody(params), heardes, callBack);
    }

    public static void doPostFile(String url, Map<String, String> params, ByteContentFile file, HttpCallBack callBack) {
        requestPost(url, getBody(params, file), null, callBack);
    }

    public static void doPostFile(String url, Map<String, String> params, ByteContentFile file, Map<String, String> headers, HttpCallBack callBack) {
        requestPost(url, getBody(params, file), headers, callBack);
    }

    public static void doPostFiles(String url, Map<String, String> params, List<ByteContentFile> files, Map<String, String> headers, HttpCallBack callBack) {
        requestPost(url, getBody(params, files), headers, callBack);
    }

    public static void requestPost(String url, RequestBody body, Map<String, String> headers, HttpCallBack callBack) {
        Request.Builder builder = new Request.Builder();
        builder.url(url);
        if (headers != null && headers.size() > 0)
            builder.headers(getHeaders(headers));
        builder.post(body);
        request(builder.build(), callBack);
    }

    public static <T> T requestPost(String url, RequestBody body, Map<String, String> headers, TypeReference<T> type) {
        Request.Builder builder = new Request.Builder();
        builder.url(url);
        if (headers != null && headers.size() > 0)
            builder.headers(getHeaders(headers));
        builder.post(body);
        return request(builder.build(), type);
    }

    public static <T> T request(Request request, TypeReference<T> type) {
        try {
            Response response = getClient().newCall(request).execute();
            String result = response.body().string();
            if (!StringUtil.isEmpty(result)) {
                TimberUtil.d("OkHttp:", result);
                return JsonUtil.fromJson(result, type);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    public static void request(Request request, HttpCallBack callBack) {
        getClient().newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                if (callBack != null) callBack.onException(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();
                if (!StringUtil.isEmpty(result)) {
                    TimberUtil.d("OkHttp:", result);
                    if (callBack != null) {
                        try {
                            Type type = ((ParameterizedType) callBack.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
                            callBack.onSuccess(JsonUtil.fromJson(result, type));
                        } catch (Exception e) {
                            callBack.onException(e);
                        }
                    }
                }
            }
        });
    }

    public static Headers getHeaders(Map<String, ?> headers) {
        Headers.Builder builder = new Headers.Builder();
        for (Map.Entry<String, ?> entry : headers.entrySet()) {
            TimberUtil.d("OkHttp:", entry.getKey() + ":" + entry.getValue().toString());
            builder.add(entry.getKey(), urlEncoderUTF8(entry.getValue().toString()));
        }
        return builder.build();
    }

    public static RequestBody getBody(Map<String, ?> params) {
        FormBody.Builder bodyBuilder = new FormBody.Builder();
        for (Map.Entry<String, ?> entry : params.entrySet()) {
            TimberUtil.d("OkHttp:", entry.getKey() + ":" + entry.getValue().toString());
            bodyBuilder.add(entry.getKey(), urlEncoderUTF8(entry.getValue().toString()));
        }
        return bodyBuilder.build();
    }


    public static RequestBody getBody(Map<String, ?> params, ByteContentFile file) {
        MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
        bodyBuilder.setType(MultipartBody.FORM);
        if (params != null && params.size() > 0) {
            for (Map.Entry<String, ?> entry : params.entrySet()) {
                TimberUtil.d("OkHttp:", entry.getKey() + ":" + entry.getValue().toString());
                bodyBuilder.addFormDataPart(entry.getKey(), urlEncoderUTF8(entry.getValue().toString()));
            }
        }
        if (file != null && file.getContent() != null && file.getContent().length > 0) {
            bodyBuilder.addFormDataPart(file.getReqName(), file.getFileName(), RequestBody.create(MediaType.parse("application/octet-stream"), file.getContent()));
        }
        return bodyBuilder.build();
    }

    public static RequestBody getBody(Map<String, ?> params, List<ByteContentFile> fileList) {
        MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
        bodyBuilder.setType(MultipartBody.FORM);
        for (Map.Entry<String, ?> entry : params.entrySet()) {
            TimberUtil.d("OkHttp:", entry.getKey() + ":" + entry.getValue().toString());
            bodyBuilder.addFormDataPart(entry.getKey(), urlEncoderUTF8(entry.getValue().toString()));
        }
        for (ByteContentFile byteContentFile : fileList) {
            bodyBuilder.addFormDataPart(byteContentFile.getReqName(), byteContentFile.getFileName(), RequestBody.create(MediaType.parse("application/octet-stream"), byteContentFile.getContent()));
        }
        return bodyBuilder.build();
    }

    public static RequestBody getBody(ByteContentFile file) {
        MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
        bodyBuilder.setType(MultipartBody.FORM);
        RequestBody requestBody = new RequestBody() {
            @Override
            public MediaType contentType() {
                return null;
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                OutputStream os = sink.outputStream();
                os.write(file.getContent());
            }
        };
        bodyBuilder.addFormDataPart(file.getReqName(), file.getFileName(), requestBody);
        return bodyBuilder.build();
    }

    public static String parseUrlRequest(Map<String, ?> params) {
        StringBuffer buffer = new StringBuffer();
        for (Map.Entry<String, ?> entry : params.entrySet()) {
            if (buffer.length() > 0) buffer.append("&");
            TimberUtil.d("OkHttp:", entry.getKey() + ":" + entry.getValue().toString());
            buffer.append(String.format("%s=%s", urlEncoderUTF8(entry.getKey()), urlEncoderUTF8(entry.getValue().toString())));
        }
        return buffer.toString();
    }

    public static String urlEncoderUTF8(String s) {
        try {
            return URLEncoder.encode(s, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return s;
    }

}
